#include "main.h"
#include "server.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifndef socketlen_t
    #define socketlen_t size_t
#endif

struct sockaddr_in echoServAddr;

fd_set master; //master file descriptor
fd_set read_fds; // tmp file descriptor for select
int newfd; // newly accept()ed socket descriptor

int createServerSocket(){
    int success = -1;
    int yes = 1;
    //struct sockaddr_in * servSock;

    FD_ZERO(&master);
    FD_ZERO(&read_fds);
    
    socketdescriptor = socket(PF_INET,SOCK_STREAM, IPPROTO_TCP);
    if(socketdescriptor < 0){
        perror("Error creating server socket");
        return -1;
    }
    
    // lose the pesky "address already in use" error message
    if (setsockopt(socketdescriptor, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
        perror("Error setsockopt");
        exit(1);
    }
    return 0;
}

int bindServerSocket(){
    int success = -1;
    //struct sockaddr_in echoServAddr;
    
    echoServAddr.sin_family = AF_INET;
    echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    echoServAddr.sin_port = htons(MYPORT);
    
    success = bind(socketdescriptor, (struct sockaddr *)&echoServAddr, sizeof(echoServAddr));
    if(success < 0){
        perror("Error binding server socket");
        return -1;
    }
    return 0;
}

int setSocketToListen(){
    int success = -1;
    success = listen(socketdescriptor,MAX_PENDING);
    if(success < 0){
        perror("Error setting server to listen");
        return -1;
    }
    return 0;
}

int startServer(){  
    pid_t  pid;
    int acceptSocket;
    struct socketAddress *clientAddr;
    //struct socketAddress *remoteaddr;
    socketlen_t *clientAddrLen;
    int buf_size=sizeof(struct atm_msg);        /* a 1K buffer */
    char buffer[buf_size];
    int nbytes = -1;
    
    int fdmax;
    
    int i, j;

    FD_SET(socketdescriptor, &master);
    fdmax = socketdescriptor;
    printf("Server running...\n");
    clientAddrLen = (socketlen_t*) sizeof(clientAddr);
    /**  MAIN Loop *******************************/
    for(isServerRunning = 1;isServerRunning == 1;){
    
	read_fds = master;
	if(select(fdmax+1, &read_fds, NULL,NULL,NULL) == -1){
            perror("select");
            exit(1);
        }
		
	//*** TODO check to see is something came in */
        
        // run through the existing connections looking for data to read
        for(i = 0; i <= fdmax; i++) {
            if (FD_ISSET(i, &read_fds)) { // we got one!!
                if (i == socketdescriptor) {
                    if ((newfd = accept(socketdescriptor, (struct sockaddr *)&clientAddr, (socklen_t *)&clientAddrLen)) == -1) {
                        perror("accept");
                    } 
                    else {
                        FD_SET(newfd, &master); // add to master set
                        if (newfd > fdmax) { // keep track of the maximum
                            fdmax = newfd;
                        }
						printf("New server connected..\n");
                        //printf("selectserver: new connection from %s on socket %d\n",
                        //    inet_ntoa((struct sockaddr_in*)&clientAddr.sin_addr), newfd);
                    }
                }
                else{
                    
                    // handle data from a client
                    if((nbytes = recv(i, buffer, buf_size,NO_FLAG)) <= 0){
                        // got error or connection closed by client
                        if (nbytes == 0) {
                            // connection closed
                            printf("selectserver: socket %d hung up\n", i);
                        }
                        else{
                            perror("recv");
                        }
                        
                        close(i); // bye!
                        FD_CLR(i, &master); // remove from master set
                    }
                    else{
                        // we got some data from a client
                        for(j = 0; j <= fdmax; j++) {
                            // send to everyone!
                            if (FD_ISSET(j, &master)) {
                                // except the listener and ourselves
                                if ((j != socketdescriptor) &&( j != i)) {
				}
				else if(i == j){
				    printf("data sent to client\n");
				    pid_t  pid;
				    pid = fork();    
				    if(pid>0){
				       //printf("close parent..\n");
				    }
				    else{ /**  child */
				        struct atm_msg msg;
                                        memcpy((void*)&msg,(void*)&buffer,buf_size);
                                        processMessage(&msg);
                                        send(j,(void*)&msg,sizeof(msg),NO_FLAG);
                                        _exit(1); // kill child when finished
				    }
				}
                            }
                        }
                    }
                }
            }
        }
        
        
    }
    return 0;
}

void processMessage(struct atm_msg* msg){

	int account_number = -1;
	account_number = atoi(msg->account_number);
	long int balance;
	
	
	//Check account number
	char line[80];
	FILE *account_file;
	
	while(isFileInUse()); // Poll for file to be usable
	setFileInUse(1);
	
	
	account_file = fopen(SERVER_FILE, "r"); 
	int isFound = 0;
	while(fgets(line,80,account_file) && !isFound){
		int acc_num;
		char name[50];
		char amount[33];

		char *pch;
		
		pch=strtok(line,"|");
		char tmp[33];
		strcpy(tmp,pch);
		acc_num = atoi(tmp);
		
    	pch = strtok (NULL,"|");
		strcpy(name,pch);
		
    	pch = strtok (NULL, "|");
    	strcpy(amount,pch);
			
		if(account_number == acc_num){
			isFound = 1;
			strcpy(msg->name,name);
			balance = atol(amount);
		
		}
	
	}
	fclose (account_file);
	setFileInUse(0); // Unlock file
	
	if(isFound == 0){
		msg->command = RESPONSE;
		msg->issuccess = ERROR;
		strcpy(msg->msg,"The account number you entered is invalid\n");
	}
	else{
		char cmd = msg->command;
		if (cmd == DEPOSIT){ 
			long int deposit_amount = atol(msg->amount);
			if(deposit_amount < 0l){
				sprintf(msg->amount, "%ld",balance);
				sprintf(msg->msg,"You must deposit a positive amount\n");
				msg->command = RESPONSE;
				msg->issuccess = ERROR;
			}
			else if((deposit_amount + balance) > 100000){
				sprintf(msg->amount, "%ld",balance);
				sprintf(msg->msg,"Sorry this account cannot exceed $100,000\n");
				msg->command = RESPONSE;
				msg->issuccess = ERROR;
			}
			else{
				long int new_amount = deposit_amount + balance;
				sprintf(msg->amount, "%ld",new_amount);
				sprintf(msg->msg,"Thank you your deposit was successful\n");
				msg->command = RESPONSE;
				msg->issuccess = SUCCESS;

				update_account(msg);
			}
		}
		else if (cmd == WITHDRAW){
			long int withdraw_amount = atol(msg->amount);
			if(withdraw_amount < 0){
				sprintf(msg->amount, "%ld",balance);
				sprintf(msg->msg,"You must withdraw a positive amount\n");
				msg->command = RESPONSE;
				msg->issuccess = ERROR;
			}
			else if((balance - withdraw_amount) < 0){
				sprintf(msg->amount, "%ld",balance);
				sprintf(msg->msg,"Sorry this account cannot go below $0\n");
				msg->command = RESPONSE;
				msg->issuccess = ERROR;
			}
			else{
				long int new_amount = balance - withdraw_amount;
				sprintf(msg->amount, "%ld",new_amount);
				sprintf(msg->msg,"Thank you your withdraw was successful\n");
				msg->command = RESPONSE;
				msg->issuccess = SUCCESS;
				{
					update_account(msg);
				}
			}
		}
		else if (cmd == BALANCE){
			sprintf(msg->amount, "%ld",balance);
			sprintf(msg->msg,"Thank you for checking your balance\n");
			msg->command = RESPONSE;
			msg->issuccess = SUCCESS;
		}
		
	}	
}



int update_account(struct atm_msg* msg){
	int success = -1;
	int account_number = -1;
	account_number = atoi(msg->account_number);
	
	while(isFileInUse()); // Poll for file to be usable
	setFileInUse(1);
	
	size_t filesize = 0;
	size_t allocatedsize = 50;
	char *file_buffer;
	file_buffer = (char*)malloc(sizeof(char)*allocatedsize);

	char line[80];
	FILE *account_file;
	
	account_file = fopen(SERVER_FILE, "r+");
	int isFound = 0;
	while(fgets(line,80,account_file)){
		int acc_num;
		char name[50];
		char amount[33];

		char *pch;
		
		pch=strtok(line,"|");
		char acc_str[33];
		strcpy(acc_str,pch);
		acc_num = atoi(acc_str);
		
    	pch = strtok (NULL,"|");
		strcpy(name,pch);
		
    	pch = strtok (NULL, "|");
    	strcpy(amount,pch);
			
		if(account_number == acc_num){
			isFound = 1;
			strcpy(msg->name,name);  // Know the users name
			strcpy(amount,msg->amount);  // Get new balance
		}
		
		if(!isFound){
			if(allocatedsize > (strlen(acc_str)+strlen(name)+strlen(amount)+filesize+4)){
				strcat(file_buffer,acc_str);strcat(file_buffer,"|");
				strcat(file_buffer,name);strcat(file_buffer,"|");
				strcat(file_buffer,amount);strcat(file_buffer,"|\n");
				filesize += strlen(acc_str)+strlen(name)+strlen(amount)+4;
			}
			else{
				allocatedsize *= 2;
				file_buffer = (char*)realloc(file_buffer, sizeof(char)*allocatedsize);				
				strcat(file_buffer,acc_str);strcat(file_buffer,"|");
				strcat(file_buffer,name);strcat(file_buffer,"|");
				strcat(file_buffer,amount);strcat(file_buffer,"|\n");				
				filesize += strlen(acc_str)+strlen(name)+strlen(amount)+4;
			}
		}
		else{
			if(allocatedsize > (strlen(acc_str)+strlen(name)+strlen(amount)+filesize+4)){
				strcat(file_buffer,acc_str);strcat(file_buffer,"|");
				strcat(file_buffer,name);strcat(file_buffer,"|");
				strcat(file_buffer,amount);strcat(file_buffer,"|\n");
				filesize += strlen(acc_str)+strlen(name)+strlen(amount)+4;
			}
			else{
				allocatedsize *= 2;
				file_buffer = (char*)realloc(file_buffer, sizeof(char)*allocatedsize);				
				strcat(file_buffer,acc_str);strcat(file_buffer,"|");
				strcat(file_buffer,name);strcat(file_buffer,"|");
				strcat(file_buffer,amount);strcat(file_buffer,"|\n");				
				filesize += strlen(acc_str)+strlen(name)+strlen(amount)+4;
			}
		}
		isFound = 0;
	}
	// EOF reached
	// Close and reopen the file to rewrite it
	fclose (account_file);	
	
	account_file = fopen(SERVER_FILE, "w+");
	success = fprintf(account_file,file_buffer);
	if(success < 0)
		perror("Error rewriting account.txt");
		
	fclose (account_file);	
	
	
	setFileInUse(0);
	free(file_buffer);
	return 0;
}

int setFileInUse(int inUse){
	_isFileInUse = inUse;
}

int isFileInUse(){
	return _isFileInUse;
}


